package com.yhy.wxzn.wcs.device.plc.task;

import cn.hutool.core.util.StrUtil;
import com.yhy.wxzn.common.enums.WcsDeviceTypeEnums;
import com.yhy.wxzn.common.utils.SpringUtil;
import com.yhy.wxzn.wcs.device.plc.task.exception.PLCInterruptException;
import com.yhy.wxzn.wcs.device.rgv.bean.Coord;
import com.yhy.wxzn.wcs.device.rgv.bean.Lifter;
import com.yhy.wxzn.wcs.device.rgv.bean.Node;
import com.yhy.wxzn.wcs.device.rgv.core.lock.LockNodeManager;
import com.yhy.wxzn.wcs.device.rgv.core.plc.ToLifer;
import com.yhy.wxzn.wcs.entity.*;
import com.yhy.wxzn.wcs.enums.LifterLocation;
import com.yhy.wxzn.wcs.enums.PlcTaskProgress;
import com.yhy.wxzn.wcs.enums.TaskStatus;
import com.yhy.wxzn.wcs.enums.TaskType;
import com.yhy.wxzn.wcs.service.impl.*;
import com.yhy.wxzn.wcs.wcsWmsCommunication.ToWmsImpl;
import com.yhy.wxzn.wcs.wcsWmsCommunication.bean.TaskStatusRtn;
import com.yhy.wxzn.wcs.wcsWmsCommunication.bean.WmsTaskStatus;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.Nullable;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import static com.yhy.wxzn.wcs.SystemInit.Lifter_Connection;


@Slf4j
public class PlcTaskThread extends Thread{

    public final static ConcurrentHashMap<Integer, Integer> PLC_INTERRUPT_CACHE = new ConcurrentHashMap<>();


    private WcsInboundConfigServiceImpl inboundConfigService = SpringUtil.getBean(WcsInboundConfigServiceImpl.class);
    private WcsInboundDetailServiceImpl inboundDetailService = SpringUtil.getBean(WcsInboundDetailServiceImpl.class);

    private WcsOutboundConfigServiceImpl outboundConfigService = SpringUtil.getBean(WcsOutboundConfigServiceImpl.class);
    private WcsOutboundDetailServiceImpl outboundDetailService = SpringUtil.getBean(WcsOutboundDetailServiceImpl.class);


    private WcsTaskServiceImpl taskService = SpringUtil.getBean(WcsTaskServiceImpl.class);
    private WcsDeviceInfoServiceImpl deviceInfoService = SpringUtil.getBean(WcsDeviceInfoServiceImpl.class);


    private ToWmsImpl toWms = SpringUtil.getBean(ToWmsImpl.class);


    private WcsTask task;


    private PlcTaskThread(WcsTask task){
        super("任务id:"+task.getTaskId() +"，PLC任务线程");
        this.task = task;
        PLC_INTERRUPT_CACHE.put(task.getTaskId(),task.getTaskId());
    }


    public static void executeTask(WcsTask task){
        new PlcTaskThread(task).start();
    }


    private void process(){

        if (Objects.equals(TaskType.IN,task.getTaskType())){

            List<WcsInboundDetail> inProgramme = findInProgramme();

            executeInProgramme(inProgramme);

        }else if (Objects.equals(TaskType.OUT,task.getTaskType())){

            List<WcsOutboundDetail> outProgramme = findOutProgramme();

            executeOutProgramme(outProgramme);

            this.task = taskService.updateTaskStatus(task, TaskStatus.FINISH);

            if (StrUtil.isNotEmpty(task.getWmsTaskId()))
                toWms.executionPhase(TaskStatusRtn.buildTaskStatusRtn(task.getWmsTaskId(), WmsTaskStatus.FINISH));
        }else if (Objects.equals(TaskType.MOVE_STATION,task.getTaskType())){

        }

    }


    private void executeInProgramme(List<WcsInboundDetail> inProgrammes) {
        if (Objects.equals(task.getPlcProgress(), PlcTaskProgress.ALL_DEVICE_COMPLETE.getTaskProgress()))
            return;

        if (Objects.equals(task.getPlcProgress(), PlcTaskProgress.WAIT.getTaskProgress()))
            task = taskService.updatePlcProgress(task, inProgrammes.get(0).getDeviceName(),null);

        for (WcsInboundDetail inProgramme : inProgrammes) {
            WcsDeviceInfo deviceInfo = deviceInfoService.findByDeviceName(inProgramme.getDeviceName());
            if (! Objects.equals(task.getPlcProgress(), deviceInfo.getId()))  continue;
            executeInProgramme(inProgramme,deviceInfo);
        }

        task = taskService.updatePlcProgress(task, null,PlcTaskProgress.ALL_DEVICE_COMPLETE.getTaskProgress());
    }

    private void executeInProgramme(WcsInboundDetail inProgramme, WcsDeviceInfo deviceInfo) {
        task = taskService.updatePlcProgress(task,inProgramme.getDeviceName(),null);
        if (WcsDeviceTypeEnums.TSL.getValue().equals(deviceInfo.getDeviceType())){

        }else  if (WcsDeviceTypeEnums.ELV.getValue().equals(deviceInfo.getDeviceType())){
            inCallLifter(inProgramme,deviceInfo);
        }
    }

    private void inCallLifter(WcsInboundDetail inProgramme, WcsDeviceInfo deviceInfo) {
        Lifter lifter = Lifter.parseLifter(deviceInfo.getLocation());


        ToLifer.getInstance(lifter).inCallLifter(task,inProgramme,deviceInfo);
    }

    private void callLifter(WcsInboundDetail programme, WcsDeviceInfo deviceInfo) {
        Lifter lifter = Lifter.parseLifter(deviceInfo.getLocation());

        ToLifer.getInstance(lifter).taskCallLifter(task,programme,deviceInfo);
    }

    private List<WcsInboundDetail> findInProgramme() {
        List<WcsInboundConfig> configs = inboundConfigService.list();

        WcsInboundConfig finalWcsInboundConfig = findWcsInboundConfig(configs);

        if (finalWcsInboundConfig == null)
            throw new RuntimeException("未找到合适入库方案");


        List<WcsInboundDetail> details = inboundDetailService.findWmsInboundDetailByConfigId(finalWcsInboundConfig.getId());


        Collections.sort(details, Comparator.comparingInt(WcsInboundDetail::getSorter));

        return details;
    }

    @Nullable
    private WcsInboundConfig findWcsInboundConfig(List<WcsInboundConfig> configs) {
        for (WcsInboundConfig config : configs) {
            String connectCoordStrs = config.getConnectCoord();

            Coord startCoord = Node.parseCoord(task.getStart());

            List<Coord> connectCoords = Arrays.stream(connectCoordStrs.split(";"))
                    .map(c -> Node.parseCoord(c)).collect(Collectors.toList());

            for (Coord connectCoord : connectCoords) {
                if (connectCoord.equals(startCoord)) {
                    return config;
                }
            }
        }
        return null;
    }

    private void executeOutProgramme(List<WcsOutboundDetail> outProgrammes) {
        if (Objects.equals(task.getPlcProgress(), PlcTaskProgress.ALL_DEVICE_COMPLETE.getTaskProgress()))
            return;

        if (Objects.equals(task.getPlcProgress(), PlcTaskProgress.WAIT.getTaskProgress()))
            task = taskService.updatePlcProgress(task, outProgrammes.get(0).getDeviceName(),null);

        for (WcsOutboundDetail outProgramme : outProgrammes) {
            WcsDeviceInfo deviceInfo = deviceInfoService.findByDeviceName(outProgramme.getDeviceName());
            if (! Objects.equals(task.getPlcProgress(), deviceInfo.getId()))  continue;
            executeOutProgramme(outProgramme,deviceInfo);
        }

        task = taskService.updatePlcProgress(task, null,PlcTaskProgress.ALL_DEVICE_COMPLETE.getTaskProgress());
    }


    private void executeOutProgramme(WcsOutboundDetail outProgramme, WcsDeviceInfo deviceInfo) {
        task = taskService.updatePlcProgress(task,outProgramme.getDeviceName(),null);
        if (WcsDeviceTypeEnums.TSL.getValue().equals(deviceInfo.getDeviceType())){

        }else  if (WcsDeviceTypeEnums.ELV.getValue().equals(deviceInfo.getDeviceType())){
            outCallLifter(outProgramme,deviceInfo);
//            callLifter(outProgramme,  deviceInfo);
        }
    }

    private void outCallLifter(WcsOutboundDetail outProgramme, WcsDeviceInfo deviceInfo) {
        Lifter lifter = Lifter.parseLifter(deviceInfo.getLocation());


        ToLifer.getInstance(lifter).outCallLifter(task,outProgramme,deviceInfo);

    }

    private void callLifter(WcsOutboundDetail programme, WcsDeviceInfo deviceInfo) {
        Lifter lifter = Lifter.parseLifter(deviceInfo.getLocation());


        ToLifer.getInstance(lifter).taskCallLifter(task,programme,deviceInfo);
    }

    private List<WcsOutboundDetail> findOutProgramme() {
        List<WcsOutboundConfig> configs = outboundConfigService.list();

        WcsOutboundConfig finalWcsOutboundConfig = findWcsOutboundConfig(configs);

        if (finalWcsOutboundConfig == null)
            throw new RuntimeException("未找到合适出库方案");


        List<WcsOutboundDetail> details = outboundDetailService.findWmsOutboundDetailByConfigId(finalWcsOutboundConfig.getId());


        Collections.sort(details, Comparator.comparingInt(WcsOutboundDetail::getSorter));

        return details;
    }

    private WcsOutboundConfig findWcsOutboundConfig(List<WcsOutboundConfig> configs) {
        for (WcsOutboundConfig config : configs) {
            String connectCoordStrs = config.getConnectCoord();

            Coord endCoord = Node.parseCoord(task.getEnd());

            List<Coord> connectCoords = Arrays.stream(connectCoordStrs.split(";"))
                    .map(c -> Node.parseCoord(c)).collect(Collectors.toList());

            for (Coord connectCoord : connectCoords) {
                if (connectCoord.equals(endCoord)) {
                    return config;
                }
            }
        }
        return null;
    }


    public static void checkPLCInterrupt(Integer key, String msg) {
        if (PLC_INTERRUPT_CACHE.get(key) == null) {
            log.error("PLC中断：" + key + ", In ->" + msg);
            throw new PLCInterruptException("PLC中断：" + key + ", In ->" + msg);
        }
    }

    public static void interceptPLC(Integer key) {
        PLC_INTERRUPT_CACHE.remove(key);
    }

    public static boolean noNeedForRecoveryPLC(Integer key) {
        return PLC_INTERRUPT_CACHE.containsKey(key);
    }



    @Override
    public void run() {
        try {
            process();
        }catch (Exception e){
            log.error(e.getMessage(),e);
            taskService.updateTaskStatus(task,TaskStatus.ERROR);
        }finally {
            interceptPLC(task.getTaskId());
        }
    }
}
